home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 30
/
Aminet 30 (1999)(Schatztruhe)[!][Apr 1999].iso
/
Aminet
/
gfx
/
misc
/
gnuplot-3.7src.lha
/
gnuplot-3.7src
/
gnuplot-3.7.lha
/
gnuplot-3.7
/
util3d.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-09-22
|
33KB
|
1,260 lines
#ifndef lint
static char *RCSid = "$Id: util3d.c,v 1.17 1998/06/18 14:55:20 ddenholm Exp $";
#endif
/* GNUPLOT - util3d.c */
/*[
* Copyright 1986 - 1993, 1998 Thomas Williams, Colin Kelley
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose with or without fee is hereby granted,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation.
*
* Permission to modify the software is granted, but not the right to
* distribute the complete modified source code. Modifications are to
* be distributed as patches to the released version. Permission to
* distribute binaries produced by compiling modified sources is granted,
* provided you
* 1. distribute the corresponding source modifications from the
* released version in the form of a patch file along with the binaries,
* 2. add special version identification to distinguish your version
* in addition to the base release version number,
* 3. provide your name and address as the primary contact for the
* support of your modified version, and
* 4. retain our contact information in regard to use of the base
* software.
* Permission to distribute the released version of the source code along
* with corresponding source modifications in the form of a patch file is
* granted with same provisions 2 through 4 for binary distributions.
*
* This software is provided "as is" without express or implied warranty
* to the extent permitted by applicable law.
]*/
/*
* 19 September 1992 Lawrence Crowl (crowl@cs.orst.edu)
* Added user-specified bases for log scaling.
*
* 3.6 - split graph3d.c into graph3d.c (graph),
* util3d.c (intersections, etc)
* hidden3d.c (hidden-line removal code)
*
*/
#include "plot.h"
#include "setshow.h"
extern int xleft,xright,ybot,ytop;
extern int hidden_active; /* HBB 980324: hidden_no_update was unused here */
/* ACCESS THINGS THAT OUGHT TO BE HIDDEN IN hidden3d.c - perhaps we
* can move the relevant code into hidden3d.c sometime
*/
/* Bitmap of the screen. The array for each x value is malloc-ed as needed */
extern int suppressMove;
extern double min_array[], max_array[];
extern int auto_array[], log_array[];
extern double base_array[], log_base_array[];
/* for convenience while converting to use these arrays */
#define x_min3d min_array[FIRST_X_AXIS]
#define x_max3d max_array[FIRST_X_AXIS]
#define y_min3d min_array[FIRST_Y_AXIS]
#define y_max3d max_array[FIRST_Y_AXIS]
#define z_min3d min_array[FIRST_Z_AXIS]
#define z_max3d max_array[FIRST_Z_AXIS]
#define min3d_z min_array[FIRST_Z_AXIS]
#define max3d_z max_array[FIRST_Z_AXIS]
typedef double transform_matrix[4][4];
void mat_unit(mat)
transform_matrix mat;
{
int i, j;
for (i = 0; i < 4; i++) for (j = 0; j < 4; j++)
if (i == j)
mat[i][j] = 1.0;
else
mat[i][j] = 0.0;
}
void mat_trans(tx, ty, tz, mat)
double tx, ty, tz;
transform_matrix mat;
{
mat_unit(mat); /* Make it unit matrix. */
mat[3][0] = tx;
mat[3][1] = ty;
mat[3][2] = tz;
}
void mat_scale(sx, sy, sz, mat)
double sx, sy, sz;
transform_matrix mat;
{
mat_unit(mat); /* Make it unit matrix. */
mat[0][0] = sx;
mat[1][1] = sy;
mat[2][2] = sz;
}
void mat_rot_x(teta, mat)
double teta;
transform_matrix mat;
{
double cos_teta, sin_teta;
teta *= Pi / 180.0;
cos_teta = cos(teta);
sin_teta = sin(teta);
mat_unit(mat); /* Make it unit matrix. */
mat[1][1] = cos_teta;
mat[1][2] = -sin_teta;
mat[2][1] = sin_teta;
mat[2][2] = cos_teta;
}
void mat_rot_y(teta, mat)
double teta;
transform_matrix mat;
{
double cos_teta, sin_teta;
teta *= Pi / 180.0;
cos_teta = cos(teta);
sin_teta = sin(teta);
mat_unit(mat); /* Make it unit matrix. */
mat[0][0] = cos_teta;
mat[0][2] = -sin_teta;
mat[2][0] = sin_teta;
mat[2][2] = cos_teta;
}
void mat_rot_z(teta, mat)
double teta;
transform_matrix mat;
{
double cos_teta, sin_teta;
teta *= Pi / 180.0;
cos_teta = cos(teta);
sin_teta = sin(teta);
mat_unit(mat); /* Make it unit matrix. */
mat[0][0] = cos_teta;
mat[0][1] = -sin_teta;
mat[1][0] = sin_teta;
mat[1][1] = cos_teta;
}
/* Multiply two transform_matrix. Result can be one of two operands. */
void mat_mult(mat_res, mat1, mat2)
transform_matrix mat_res, mat1, mat2;
{
int i, j, k;
transform_matrix mat_res_temp;
for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) {
mat_res_temp[i][j] = 0;
for (k = 0; k < 4; k++) mat_res_temp[i][j] += mat1[i][k] * mat2[k][j];
}
for (i = 0; i < 4; i++) for (j = 0; j < 4; j++)
mat_res[i][j] = mat_res_temp[i][j];
}
/* Test a single point to be within the xleft,xright,ybot,ytop bbox.
* Sets the returned integers 4 l.s.b. as follows:
* bit 0 if to the left of xleft.
* bit 1 if to the right of xright.
* bit 2 if above of ytop.
* bit 3 if below of ybot.
* 0 is returned if inside.
*/
int clip_point(x, y)
unsigned int x, y;
{
int ret_val = 0;
if (x < xleft) ret_val |= 0x01;
if (x > xright) ret_val |= 0x02;
if (y < ybot) ret_val |= 0x04;
if (y > ytop) ret_val |= 0x08;
return ret_val;
}
/* Clip the given line to drawing coords defined as xleft,xright,ybot,ytop.
* This routine uses the cohen & sutherland bit mapping for fast clipping -
* see "Principles of Interactive Computer Graphics" Newman & Sproull page 65.
*/
void draw_clip_line(x1, y1, x2, y2)
unsigned int x1, y1, x2, y2;
{
int x, y, dx, dy, x_intr[4], y_intr[4], count, pos1, pos2;
register struct termentry *t = term;
#if defined(ATARI) || defined(MTOS)
if(x1<0||x2<0||y1<0||y2<0) return; /* temp bug fix */
#endif
pos1 = clip_point(x1, y1);
pos2 = clip_point(x2, y2);
if (pos1 || pos2) {
if (pos1 & pos2) return; /* segment is totally out. */
/* Here part of the segment MAY be inside. test the intersection
* of this segment with the 4 boundaries for hopefully 2 intersections
* in. If none are found segment is totaly out.
* Under rare circumstances there may be up to 4 intersections (e.g.
* when the line passes directly through at least one corner). In
* this case it is sufficient to take any 2 intersections (e.g. the
* first two found).
*/
count = 0;
dx = x2 - x1;
dy = y2 - y1;
/* Find intersections with the x parallel bbox lines: */
if (dy != 0) {
x = (ybot - y2) * dx / dy + x2; /* Test for ybot boundary. */
if (x >= xleft && x <= xright) {
x_intr[count] = x;
y_intr[count++] = ybot;
}
x = (ytop - y2) * dx / dy + x2; /* Test for ytop boundary. */
if (x >= xleft && x <= xright) {
x_intr[count] = x;
y_intr[count++] = ytop;
}
}
/* Find intersections with the y parallel bbox lines: */
if (dx != 0) {
y = (xleft - x2) * dy / dx + y2; /* Test for xleft boundary. */
if (y >= ybot && y <= ytop) {
x_intr[count] = xleft;
y_intr[count++] = y;
}
y = (xright - x2) * dy / dx + y2; /* Test for xright boundary. */
if (y >= ybot && y <= ytop) {
x_intr[count] = xright;
y_intr[count++] = y;
}
}
if (count >= 2) {
int x_max, x_min, y_max, y_min;
x_min = GPMIN(x1, x2);
x_max = GPMAX(x1, x2);
y_min = GPMIN(y1, y2);
y_max = GPMAX(y1, y2);
if (pos1 && pos2) { /* Both were out - update both */
x1 = x_intr[0];
y1 = y_intr[0];
x2 = x_intr[1];
y2 = y_intr[1];
}
else if (pos1) { /* Only x1/y1 was out - update only it */
if (dx * (x2 - x_intr[0]) + dy * (y2 - y_intr[0]) > 0) {
x1 = x_intr[0];
y1 = y_intr[0];
}
else {
x1 = x_intr[1];
y1 = y_intr[1];
}
}
else { /* Only x2/y2 was out - update only it */
if (dx * (x_intr[0] - x1) + dy * (y_intr[0] - y1) > 0) {
x2 = x_intr[0];
y2 = y_intr[0];
}
else {
x2 = x_intr[1];
y2 = y_intr[1];
}
}
if (x1 < x_min || x1 > x_max ||
x2 < x_min || x2 > x_max ||
y1 < y_min || y1 > y_max ||
y2 < y_min || y2 > y_max) return;
}
else
return;
}
#ifndef LITE
if(hidden3d && hidden_active && draw_surface)
{
draw_line_hidden(x1, y1, x2, y2);
return;
};
#endif /* not LITE */
if(!suppressMove) (*t->move)(x1,y1);
(*t->vector)(x2,y2);
}
/* And text clipping routine. */
void clip_put_text(x, y, str)
unsigned int x,y;
char *str;
{
register struct termentry *t = term;
if (clip_point(x, y)) return;
(*t->put_text)(x,y,str);
}
/* seems sensible to put the justification in here too..? */
void clip_put_text_just(x,y,str,just)
unsigned int x,y;
char *str;
enum JUSTIFY just;
{
register struct termentry *t = term;
if (clip_point(x,y)) return;
if (!(*t->justify_text)(just)) {
assert(CENTRE == 1 && RIGHT == 2);
x -= (t->h_char * strlen(str) * just)/2;
}
(*t->put_text)(x,y,str);
}
/* Clip the given line to drawing coords defined as xleft,xright,ybot,ytop.
* This routine uses the cohen & sutherland bit mapping for fast clipping -
* see "Principles of Interactive Computer Graphics" Newman & Sproull page 65.
*/
int clip_line(x1, y1, x2, y2)
int *x1, *y1, *x2, *y2;
{
int x, y, dx, dy, x_intr[4], y_intr[4], count, pos1, pos2;
int x_max, x_min, y_max, y_min;
pos1 = clip_point(*x1, *y1);
pos2 = clip_point(*x2, *y2);
if (!pos1 && !pos2) return 1; /* segment is totally in */
if (pos1 & pos2) return 0; /* segment is totally out. */
/* Here part of the segment MAY be inside. test the intersection
* of this segment with the 4 boundaries for hopefully 2 intersections
* in. If non found segment is totaly out.
*/
count = 0;
dx = *x2 - *x1;
dy = *y2 - *y1;
/* Find intersections with the x parallel bbox lines: */
if (dy != 0) {
x = (ybot - *y2) * dx / dy + *x2; /* Test for ybot boundary. */
if (x >= xleft && x <= xright) {
x_intr[count] = x;
y_intr[count++] = ybot;
}
x = (ytop - *y2) * dx / dy + *x2; /* Test for ytop boundary. */
if (x >= xleft && x <= xright) {
x_intr[count] = x;
y_intr[count++] = ytop;
}
}
/* Find intersections with the y parallel bbox lines: */
if (dx != 0) {
y = (xleft - *x2) * dy / dx + *y2; /* Test for xleft boundary. */
if (y >= ybot && y <= ytop) {
x_intr[count] = xleft;
y_intr[count++] = y;
}
y = (xright - *x2) * dy / dx + *y2; /* Test for xright boundary. */
if (y >= ybot && y <= ytop) {
x_intr[count] = xright;
y_intr[count++] = y;
}
}
if (count < 2) return 0;
if (*x1 < *x2)
x_min = *x1, x_max = *x2;
else
x_min = *x2, x_max = *x1;
if (*y1 < *y2)
y_min = *y1, y_max = *y2;
else
y_min = *y2, y_max = *y1;
if (pos1 && pos2) { /* Both were out - update both */
*x1 = x_intr[0];
*y1 = y_intr[0];
*x2 = x_intr[1];
*y2 = y_intr[1];
}
else if (pos1)
{ /* Only x1/y1 was out - update only it */
if (dx * (*x2 - x_intr[0]) + dy * (*y2 - y_intr[0]) >= 0)
{
*x1 = x_intr[0];
*y1 = y_intr[0];
}
else
{
*x1 = x_intr[1];
*y1 = y_intr[1];
}
}
else
{ /* Only x2/y2 was out - update only it */
if (dx * (x_intr[0] - *x1) + dy * (y_intr[0] - *y1) >= 0)
{
*x2 = x_intr[0];
*y2 = y_intr[0];
}
else
{
*x2 = x_intr[1];
*y2 = y_intr[1];
}
}
if (*x1 < x_min || *x1 > x_max ||
*x2 < x_min || *x2 > x_max ||
*y1 < y_min || *y1 > y_max ||
*y2 < y_min || *y2 > y_max)
return 0;
return 1;
}
/* single edge intersection algorithm */
/* Given two points, one inside and one outside the plot, return
* the point where an edge of the plot intersects the line segment defined
* by the two points.
*/
void edge3d_intersect(points, i, ex, ey, ez)
struct coordinate GPHUGE *points; /* the points array */
int i; /* line segment from point i-1 to point i */
double *ex, *ey, *ez; /* the point where it crosses an edge */
{
/* global x_min3d, x_max3d, y_min3d, y_max3d, min3d_z, max3d_z */
int count;
double ix = points[i-1].x;
double iy = points[i-1].y;
double iz = points[i-1].z;
double ox = points[i].x;
double oy = points[i].y;
double oz = points[i].z;
double x, y, z; /* possible intersection point */
if(points[i].type == INRANGE)
{
/* swap points around so that ix/ix/iz are INRANGE and ox/oy/oz are OUTRANGE */
x = ix;ix = ox;ox = x;
y = iy;iy = oy;oy = y;
z = iz;iz = oz;oz = z;
}
/* nasty degenerate cases, effectively drawing to an infinity point (?)
cope with them here, so don't process them as a "real" OUTRANGE point
If more than one coord is -VERYLARGE, then can't ratio the "infinities"
so drop out by returning the INRANGE point.
Obviously, only need to test the OUTRANGE point (coordinates) */
/* nasty degenerate cases, effectively drawing to an infinity point (?)
cope with them here, so don't process them as a "real" OUTRANGE point
If more than one coord is -VERYLARGE, then can't ratio the "infinities"
so drop out by returning FALSE */
count = 0;
if(ox == -VERYLARGE) count++;
if(oy == -VERYLARGE) count++;
if(oz == -VERYLARGE) count++;
/* either doesn't pass through 3D volume *or*
can't ratio infinities to get a direction to draw line, so return the INRANGE point */
if(count > 1){
*ex = ix;
*ey = iy;
*ez = iz;
return;
}
if(count == 1)
{
*ex = ix;
*ey = iy;
*ez = iz;
if(ox == -VERYLARGE)
{
*ex = x_min3d;
return;
}
if(oy == -VERYLARGE)
{
*ey = y_min3d;
return;
}
/* obviously oz is -VERYLARGE and (ox != -VERYLARGE && oy != -VERYLARGE) */
*ez = min3d_z;
return;
}
/*
* Can't have case (ix == ox && iy == oy && iz == oz) as one point
* is INRANGE and one point is OUTRANGE.
*/
if(ix == ox) {
if(iy == oy) {
/* line parallel to z axis */
/* assume inrange(iy, y_min3d, y_max3d) && inrange(ix, x_min3d, x_max3d) */
*ex = ix; /* == ox */
*ey = iy; /* == oy */
if (inrange(max3d_z, iz, oz))
*ez = max3d_z;
else if (inrange(min3d_z, iz, oz))
*ez = min3d_z;
else {
graph_error("error in edge3d_intersect");
}
return;
}
if(iz == oz) {
/* line parallel to y axis */
/* assume inrange(iz, min3d_z, max3d_z) && inrange(ix, x_min3d, x_max3d) */
*ex = ix; /* == ox */
*ez = iz; /* == oz */
if (inrange(y_max3d, iy, oy))
*ey = y_max3d;
else if (inrange(y_min3d, iy, oy))
*ey = y_min3d;
else {
graph_error("error in edge3d_intersect");
}
return;
}
/* nasty 2D slanted line in a yz plane */
/* does it intersect y_min3d edge */
if (inrange(y_min3d, iy, oy) && y_min3d != iy && y_min3d != oy) {
z = iz + (y_min3d-iy) * ((oz-iz) / (oy-iy));
if (inrange(z, min3d_z, max3d_z)) {
*ex = ix;
*ey = y_min3d;
*ez = z;
return;
}
}
/* does it intersect y_max3d edge */
if (inrange(y_max3d, iy, oy) && y_max3d != iy && y_max3d != oy) {
z = iz + (y_max3d-iy) * ((oz-iz) / (oy-iy));
if (inrange(z, min3d_z, max3d_z)) {
*ex = ix;
*ey = y_max3d;
*ez = z;
return;
}
}
/* does it intersect min3d_z edge */
if (inrange(min3d_z, iz, oz) && min3d_z != iz && min3d_z != oz) {
y = iy + (min3d_z-iz) * ((oy-iy) / (oz-iz));
if (inrange(y, y_min3d, y_max3d)) {
*ex = ix;
*ey = y;
*ez = min3d_z;
return;
}
}
/* does it intersect max3d_z edge */
if (inrange(max3d_z, iz, oz) && max3d_z != iz && max3d_z != oz) {
y = iy + (max3d_z-iz) * ((oy-iy) / (oz-iz));
if (inrange(y, y_min3d, y_max3d)) {
*ex = ix;
*ey = y;
*ez = max3d_z;
return;
}
}
}
if(iy == oy) {
/* already checked case (ix == ox && iy == oy) */
if(oz == iz) {
/* line parallel to x axis */
/* assume inrange(iz, min3d_z, max3d_z) && inrange(iy, y_min3d, y_max3d) */
*ey = iy; /* == oy */
*ez = iz; /* == oz */
if (inrange(x_max3d, ix, ox))
*ex = x_max3d;
else if (inrange(x_min3d, ix, ox))
*ex = x_min3d;
else {
graph_error("error in edge3d_intersect");
}
return;
}
/* nasty 2D slanted line in an xz plane */
/* does it intersect x_min3d edge */
if (inrange(x_min3d, ix, ox) && x_min3d != ix && x_min3d != ox) {
z = iz + (x_min3d-ix) * ((oz-iz) / (ox-ix));
if (inrange(z, min3d_z, max3d_z)) {
*ex = x_min3d;
*ey = iy;
*ez = z;
return;
}
}
/* does it intersect x_max3d edge */
if (inrange(x_max3d, ix, ox) && x_max3d != ix && x_max3d != ox) {
z = iz + (x_max3d-ix) * ((oz-iz) / (ox-ix));
if (inrange(z, min3d_z, max3d_z)) {
*ex = x_max3d;
*ey = iy;
*ez = z;
return;
}
}
/* does it intersect min3d_z edge */
if (inrange(min3d_z, iz, oz) && min3d_z != iz && min3d_z != oz) {
x = ix + (min3d_z-iz) * ((ox-ix) / (oz-iz));
if (inrange(x, x_min3d, x_max3d)) {
*ex = x;
*ey = iy;
*ez = min3d_z;
return;
}
}
/* does it intersect max3d_z edge */
if (inrange(max3d_z, iz, oz) && max3d_z != iz && max3d_z != oz) {
x = ix + (max3d_z-iz) * ((ox-ix) / (oz-iz));
if (inrange(x, x_min3d, x_max3d)) {
*ex = x;
*ey = iy;
*ez = max3d_z;
return;
}
}
}
if(iz == oz) {
/* already checked cases (ix == ox && iz == oz) and (iy == oy && iz == oz) */
/* nasty 2D slanted line in an xy plane */
/* assume inrange(oz, min3d_z, max3d_z) */
/* does it intersect x_min3d edge */
if (inrange(x_min3d, ix, ox) && x_min3d != ix && x_min3d != ox) {
y = iy + (x_min3d-ix) * ((oy-iy) / (ox-ix));
if (inrange(y, y_min3d, y_max3d)) {
*ex = x_min3d;
*ey = y;
*ez = iz;
return;
}
}
/* does it intersect x_max3d edge */
if (inrange(x_max3d, ix, ox) && x_max3d != ix && x_max3d != ox) {
y = iy + (x_max3d-ix) * ((oy-iy) / (ox-ix));
if (inrange(y, y_min3d, y_max3d)) {
*ex = x_max3d;
*ey = y;
*ez = iz;
return;
}
}
/* does it intersect y_min3d edge */
if (inrange(y_min3d, iy, oy) && y_min3d != iy && y_min3d != oy) {
x = ix + (y_min3d-iy) * ((ox-ix) / (oy-iy));
if (inrange(x, x_min3d, x_max3d)) {
*ex = x;
*ey = y_min3d;
*ez = iz;
return;
}
}
/* does it intersect y_max3d edge */
if (inrange(y_max3d, iy, oy) && y_max3d != iy && y_max3d != oy) {
x = ix + (y_max3d-iy) * ((ox-ix) / (oy-iy));
if (inrange(x, x_min3d, x_max3d)) {
*ex = x;
*ey = y_max3d;
*ez = iz;
return;
}
}
}
/* really nasty general slanted 3D case */
/* does it intersect x_min3d edge */
if (inrange(x_min3d, ix, ox) && x_min3d != ix && x_min3d != ox) {
y = iy + (x_min3d-ix) * ((oy-iy) / (ox-ix));
z = iz + (x_min3d-ix) * ((oz-iz) / (ox-ix));
if (inrange(y, y_min3d, y_max3d) && inrange(z, min3d_z, max3d_z)) {
*ex = x_min3d;
*ey = y;
*ez = z;
return;
}
}
/* does it intersect x_max3d edge */
if (inrange(x_max3d, ix, ox) && x_max3d != ix && x_max3d != ox) {
y = iy + (x_max3d-ix) * ((oy-iy) / (ox-ix));
z = iz + (x_max3d-ix) * ((oz-iz) / (ox-ix));
if (inrange(y, y_min3d, y_max3d) && inrange(z, min3d_z, max3d_z)) {
*ex = x_max3d;
*ey = y;
*ez = z;
return;
}
}
/* does it intersect y_min3d edge */
if (inrange(y_min3d, iy, oy) && y_min3d != iy && y_min3d != oy) {
x = ix + (y_min3d-iy) * ((ox-ix) / (oy-iy));
z = iz + (y_min3d-iy) * ((oz-iz) / (oy-iy));
if (inrange(x, x_min3d, x_max3d) && inrange(z, min3d_z, max3d_z)) {
*ex = x;
*ey = y_min3d;
*ez = z;
return;
}
}
/* does it intersect y_max3d edge */
if (inrange(y_max3d, iy, oy) && y_max3d != iy && y_max3d != oy) {
x = ix + (y_max3d-iy) * ((ox-ix) / (oy-iy));
z = iz + (y_max3d-iy) * ((oz-iz) / (oy-iy));
if (inrange(x, x_min3d, x_max3d) && inrange(z, min3d_z, max3d_z)) {
*ex = x;
*ey = y_max3d;
*ez = z;
return;
}
}
/* does it intersect min3d_z edge */
if (inrange(min3d_z, iz, oz) && min3d_z != iz && min3d_z != oz) {
x = ix + (min3d_z-iz) * ((ox-ix) / (oz-iz));
y = iy + (min3d_z-iz) * ((oy-iy) / (oz-iz));
if (inrange(x, x_min3d, x_max3d) && inrange(y, y_min3d, y_max3d)) {
*ex = x;
*ey = y;
*ez = min3d_z;
return;
}
}
/* does it intersect max3d_z edge */
if (inrange(max3d_z, iz, oz) && max3d_z != iz && max3d_z != oz) {
x = ix + (max3d_z-iz) * ((ox-ix) / (oz-iz));
y = iy + (max3d_z-iz) * ((oy-iy) / (oz-iz));
if (inrange(x, x_min3d, x_max3d) && inrange(y, y_min3d, y_max3d)) {
*ex = x;
*ey = y;
*ez = max3d_z;
return;
}
}
/* If we reach here, the inrange point is on the edge, and
* the line segment from the outrange point does not cross any
* other edges to get there. In this case, we return the inrange
* point as the 'edge' intersection point. This will basically draw
* line.
*/
*ex = ix;
*ey = iy;
*ez = iz;
return;
}
/* double edge intersection algorithm */
/* Given two points, both outside the plot, return
* the points where an edge of the plot intersects the line segment defined
* by the two points. There may be zero, one, two, or an infinite number
* of intersection points. (One means an intersection at a corner, infinite
* means overlaying the edge itself). We return FALSE when there is nothing
* to draw (zero intersections), and TRUE when there is something to
* draw (the one-point case is a degenerate of the two-point case and we do
* not distinguish it - we draw it anyway).
*/
TBOOLEAN /* any intersection? */
two_edge3d_intersect(points, i, lx, ly, lz)
struct coordinate GPHUGE *points; /* the points array */
int i; /* line segment from point i-1 to point i */
double *lx, *ly, *lz; /* lx[2], ly[2], lz[2]: points where it crosses edges */
{
int count;
/* global x_min3d, x_max3d, y_min3d, y_max3d, min3d_z, max3d_z */
double ix = points[i-1].x;
double iy = points[i-1].y;
double iz = points[i-1].z;
double ox = points[i].x;
double oy = points[i].y;
double oz = points[i].z;
double t[6];
double swap;
double x, y, z; /* possible intersection point */
double t_min, t_max;
/* nasty degenerate cases, effectively drawing to an infinity point (?)
cope with them here, so don't process them as a "real" OUTRANGE point
If more than one coord is -VERYLARGE, then can't ratio the "infinities"
so drop out by returning FALSE */
count = 0;
if(ix == -VERYLARGE) count++;
if(ox == -VERYLARGE) count++;
if(iy == -VERYLARGE) count++;
if(oy == -VERYLARGE) count++;
if(iz == -VERYLARGE) count++;
if(oz == -VERYLARGE) count++;
/* either doesn't pass through 3D volume *or*
can't ratio infinities to get a direction to draw line, so simply return(FALSE) */
if(count > 1){
return(FALSE);
}
if(ox == -VERYLARGE || ix == -VERYLARGE)
{
if(ix == -VERYLARGE)
{
/* swap points so ix/iy/iz don't have a -VERYLARGE component */
x = ix;ix = ox;ox = x;
y = iy;iy = oy;oy = y;
z = iz;iz = oz;oz = z;
}
/* check actually passes through the 3D graph volume */
if(ix > x_max3d && inrange(iy, y_min3d, y_max3d) && inrange(iz, min3d_z, max3d_z))
{
lx[0] = x_min3d;
ly[0] = iy;
lz[0] = iz;
lx[1] = x_max3d;
ly[1] = iy;
lz[1] = iz;
return(TRUE);
}
else {
return(FALSE);
}
}
if(oy == -VERYLARGE || iy == -VERYLARGE)
{
if(iy == -VERYLARGE)
{
/* swap points so ix/iy/iz don't have a -VERYLARGE component */
x = ix; ix = ox; ox = x;
y = iy; iy = oy; oy = y;
z = iz; iz = oz; oz = z;
}
/* check actually passes through the 3D graph volume */
if(iy > y_max3d && inrange(ix, x_min3d, x_max3d) && inrange(iz, min3d_z, max3d_z))
{
lx[0] = ix;
ly[0] = y_min3d;
lz[0] = iz;
lx[1] = ix;
ly[1] = y_max3d;
lz[1] = iz;
return(TRUE);
}
else {
return(FALSE);
}
}
if(oz == -VERYLARGE || iz == -VERYLARGE)
{
if(iz == -VERYLARGE)
{
/* swap points so ix/iy/iz don't have a -VERYLARGE component */
x = ix; ix = ox; ox = x;
y = iy; iy = oy; oy = y;
z = iz; iz = oz; oz = z;
}
/* check actually passes through the 3D graph volume */
if(iz > max3d_z && inrange(ix, x_min3d, x_max3d) && inrange(iy, y_min3d, y_max3d))
{
lx[0] = ix;
ly[0] = iy;
lz[0] = min3d_z;
lx[1] = ix;
ly[1] = iy;
lz[1] = max3d_z;
return(TRUE);
}
else {
return(FALSE);
}
}
/*
* Quick outcode tests on the 3d graph volume
*/
/*
* test z coord first --- most surface OUTRANGE points generated between
* min3d_z and z_min3d (i.e. when ticslevel is non-zero)
*/
if(GPMAX(iz,oz) < min3d_z || GPMIN(iz,oz) > max3d_z)
return(FALSE);
if(GPMAX(ix,ox) < x_min3d || GPMIN(ix,ox) > x_max3d)
return(FALSE);
if(GPMAX(iy,oy) < y_min3d || GPMIN(iy,oy) > y_max3d)
return(FALSE);
/*
* Special horizontal/vertical, etc. cases are checked and remaining
* slant lines are checked separately.
*
* The slant line intersections are solved using the parametric form
* of the equation for a line, since if we test x/y/z min/max planes explicitly
* then e.g. a line passing through a corner point (x_min,y_min,z_min)
* actually intersects all 3 planes and hence further tests would be required
* to anticipate this and similar situations.
*/
/*
* Can have case (ix == ox && iy == oy && iz == oz) as both points OUTRANGE
*/
if(ix == ox && iy == oy && iz == oz)
{
/* but as only define single outrange point, can't intersect 3D graph volume */
return(FALSE);
}
if(ix == ox) {
if(iy == oy) {
/* line parallel to z axis */
/* x and y coords must be in range, and line must span both min3d_z and max3d_z */
/* note that spanning min3d_z implies spanning max3d_z as both points OUTRANGE */
if(!inrange(ix, x_min3d, x_max3d) || !inrange(iy, y_min3d, y_max3d))
{
return(FALSE);
}
if (inrange(min3d_z, iz, oz)) {
lx[0] = ix;
ly[0] = iy;
lz[0] = min3d_z;
lx[1] = ix;
ly[1] = iy;
lz[1] = max3d_z;
return(TRUE);
} else
return(FALSE);
}
if(iz == oz) {
/* line parallel to y axis */
/* x and z coords must be in range, and line must span both y_min3d and y_max3d */
/* note that spanning y_min3d implies spanning y_max3d, as both points OUTRANGE */
if(!inrange(ix, x_min3d, x_max3d) || !inrange(iz, min3d_z, max3d_z))
{
return(FALSE);
}
if (inrange(y_min3d, iy, oy)) {
lx[0] = ix;
ly[0] = y_min3d;
lz[0] = iz;
lx[1] = ix;
ly[1] = y_max3d;
lz[1] = iz;
return(TRUE);
} else
return(FALSE);
}
/* nasty 2D slanted line in a yz plane */
if(!inrange(ox, x_min3d, x_max3d))
return(FALSE);
t[0] = (y_min3d - iy)/(oy - iy);
t[1] = (y_max3d - iy)/(oy - iy);
if(t[0] > t[1]) {
swap = t[0];t[0] = t[1];t[1] = swap;
}
t[2] = (min3d_z - iz)/(oz - iz);
t[3] = (max3d_z - iz)/(oz - iz);
if(t[2] > t[3]) {
swap = t[2];t[2] = t[3];t[3] = swap;
}
t_min = GPMAX(GPMAX(t[0],t[2]),0.0);
t_max = GPMIN(GPMIN(t[1],t[3]),1.0);
if(t_min > t_max)
return(FALSE);
lx[0] = ix;
ly[0] = iy + t_min * (oy - iy);
lz[0] = iz + t_min * (oz - iz);
lx[1] = ix;
ly[1] = iy + t_max * (oy - iy);
lz[1] = iz + t_max * (oz - iz);
/*
* Can only have 0 or 2 intersection points -- only need test one coord
*/
if(inrange(ly[0], y_min3d, y_max3d) &&
inrange(lz[0], min3d_z, max3d_z))
{
return(TRUE);
}
return(FALSE);
}
if(iy == oy) {
/* already checked case (ix == ox && iy == oy) */
if(oz == iz) {
/* line parallel to x axis */
/* y and z coords must be in range, and line must span both x_min3d and x_max3d */
/* note that spanning x_min3d implies spanning x_max3d, as both points OUTRANGE */
if(!inrange(iy, y_min3d, y_max3d) || !inrange(iz, min3d_z, max3d_z))
{
return(FALSE);
}
if (inrange(x_min3d, ix, ox)) {
lx[0] = x_min3d;
ly[0] = iy;
lz[0] = iz;
lx[1] = x_max3d;
ly[1] = iy;
lz[1] = iz;
return(TRUE);
} else
return(FALSE);
}
/* nasty 2D slanted line in an xz plane */
if(!inrange(oy, y_min3d, y_max3d))
return(FALSE);
t[0] = (x_min3d - ix)/(ox - ix);
t[1] = (x_max3d - ix)/(ox - ix);
if(t[0] > t[1]) {
swap = t[0];t[0] = t[1];t[1] = swap;
}
t[2] = (min3d_z - iz)/(oz - iz);
t[3] = (max3d_z - iz)/(oz - iz);
if(t[2] > t[3]) {
swap = t[2];t[2] = t[3];t[3] = swap;
}
t_min = GPMAX(GPMAX(t[0],t[2]),0.0);
t_max = GPMIN(GPMIN(t[1],t[3]),1.0);
if(t_min > t_max)
return(FALSE);
lx[0] = ix + t_min * (ox - ix);
ly[0] = iy;
lz[0] = iz + t_min * (oz - iz);
lx[1] = ix + t_max * (ox - ix);
ly[1] = iy;
lz[1] = iz + t_max * (oz - iz);
/*
* Can only have 0 or 2 intersection points -- only need test one coord
*/
if(inrange(lx[0], x_min3d, x_max3d) &&
inrange(lz[0], min3d_z, max3d_z))
{
return(TRUE);
}
return(FALSE);
}
if(iz == oz) {
/* already checked cases (ix == ox && iz == oz) and (iy == oy && iz == oz) */
/* nasty 2D slanted line in an xy plane */
if(!inrange(oz, min3d_z, max3d_z))
return(FALSE);
t[0] = (x_min3d - ix)/(ox - ix);
t[1] = (x_max3d - ix)/(ox - ix);
if(t[0] > t[1]) {
swap = t[0];t[0] = t[1];t[1] = swap;
}
t[2] = (y_min3d - iy)/(oy - iy);
t[3] = (y_max3d - iy)/(oy - iy);
if(t[2] > t[3]) {
swap = t[2];t[2] = t[3];t[3] = swap;
}
t_min = GPMAX(GPMAX(t[0],t[2]),0.0);
t_max = GPMIN(GPMIN(t[1],t[3]),1.0);
if(t_min > t_max)
return(FALSE);
lx[0] = ix + t_min * (ox - ix);
ly[0] = iy + t_min * (oy - iy);
lz[0] = iz;
lx[1] = ix + t_max * (ox - ix);
ly[1] = iy + t_max * (oy - iy);
lz[1] = iz;
/*
* Can only have 0 or 2 intersection points -- only need test one coord
*/
if(inrange(lx[0], x_min3d, x_max3d) &&
inrange(ly[0], y_min3d, y_max3d))
{
return(TRUE);
}
return(FALSE);
}
/* really nasty general slanted 3D case */
/*
Solve parametric equation
(ix, iy, iz) + t (diff_x, diff_y, diff_z)
where 0.0 <= t <= 1.0 and
diff_x = (ox - ix);
diff_y = (oy - iy);
diff_z = (oz - iz);
*/
t[0] = (x_min3d - ix)/(ox - ix);
t[1] = (x_max3d - ix)/(ox - ix);
if(t[0] > t[1]) {
swap = t[0];t[0] = t[1];t[1] = swap;
}
t[2] = (y_min3d - iy)/(oy - iy);
t[3] = (y_max3d - iy)/(oy - iy);
if(t[2] > t[3]) {
swap = t[2];t[2] = t[3];t[3] = swap;
}
t[4] = (iz == oz) ? 0.0 : (min3d_z - iz)/(oz - iz);
t[5] = (iz == oz) ? 1.0 : (max3d_z - iz)/(oz - iz);
if(t[4] > t[5]) {
swap = t[4];t[4] = t[5];t[5] = swap;
}
t_min = GPMAX(GPMAX(t[0],t[2]),GPMAX(t[4],0.0));
t_max = GPMIN(GPMIN(t[1],t[3]),GPMIN(t[5],1.0));
if(t_min > t_max)
return(FALSE);
lx[0] = ix + t_min * (ox - ix);
ly[0] = iy + t_min * (oy - iy);
lz[0] = iz + t_min * (oz - iz);
lx[1] = ix + t_max * (ox - ix);
ly[1] = iy + t_max * (oy - iy);
lz[1] = iz + t_max * (oz - iz);
/*
* Can only have 0 or 2 intersection points -- only need test one coord
*/
if(inrange(lx[0], x_min3d, x_max3d) &&
inrange(ly[0], y_min3d, y_max3d) &&
inrange(lz[0], min3d_z, max3d_z))
{
return(TRUE);
}
return(FALSE);
}